#include "game.h"

#include "gameobjectcollection.h"

#include "dma.h"
#include "ms3dmodel.h"
#include "pipeline.h"
#include "sps2wrap.h"
#include "texture.h"
#include "vumanager.h"

// These two external function pointers are exported by the VU code
// and we can now use them as pointers to the compiled VU data
extern "C" uint64 VU_vu1code_start;
extern "C" uint64 VU_vu1code_end;

Game::Game() {
    this->contentManager = new ContentManager();
    this->fps = 0.0f;
    this->isRunning = false;
    this->gameObjects = new GameObjectCollection();
    this->lightManager = new LightManager();
}

Game::~Game(void) {
    delete this->contentManager;
    delete this->gameObjects;
    delete this->lightManager;
}

ContentManager* Game::GetContentManager() const {
    return this->contentManager;
}

float Game::GetFPS() const {
    return this->fps;
}

void Game::SetFPS(float val) {
    this->fps = val;
}

GameObjectCollection* Game::GetGameObjects() const {
    return this->gameObjects;
}

LightManager* Game::GetLightManager() const {
    return this->lightManager;
}

bool Game::IsRunning() const {
    return this->isRunning;
}

void Game::Initialize() {
    int n = this->gameObjects->GetCount();
    for (int i = 0; i < n; i++) {
        this->gameObjects->GetItem(i)->Initialize();
    }

    this->LoadContent();
}

void Game::Draw(float timeDelta) {
    int n = this->gameObjects->GetCount();
    for (int i = 0; i < n; i++) {
        this->gameObjects->GetItem(i)->Draw(timeDelta);
    }
}

void Game::LoadContent() {
    int n = this->gameObjects->GetCount();
    for (int i = 0; i < n; i++) {
        this->gameObjects->GetItem(i)->LoadContent();
    }
}

void Game::Run() {
    // Make sure these four lines are put first since some of the
    // other classes need the managers to be initialised so they
    // can pick up the correct data.
    SPS2Manager.Initialise(4096);	// 4096 * 4K Pages = 16MB Total
    VIFStaticDMA.Initialise(3072);	// 1536 * 4K Pages = 6MB Static DMA
    VIFDynamicDMA.Initialise(256);	// 256 * 4K Pages * 2 Buffers =
    // 2MB Dynamic DMA
    Pipeline.Initialise();			// Initialise graphics pipline class

    // Initialise Lighting
    // Three lights and Ambient light
    //							Direction vector				  Colour
    Pipeline.SetLight1(Vector4( 1.0f, 0.0f, 0.0f, 0.0f), Vector4(0.0f,0.0f,0.0f,0.0f));
    Pipeline.SetLight2(Vector4( 1.0f, 0.0f, 0.0f, 0.0f), Vector4(0.0f,0.0f,0.0f,0.0f));
    Pipeline.SetLight3(Vector4( 1.0f, 0.0f, 0.0f, 0.0f), Vector4(0.0f,0.0f,0.0f,0.0f));
    //                            Colour
    Pipeline.SetAmbient(Vector4(255.0f,255.0f,255.0f,0.0f));

    // Initialise the VU1 manager class
    CVU1MicroProgram VU1MicroCode(&VU_vu1code_start, &VU_vu1code_end);

    // Upload the microcode
    VU1MicroCode.Upload();

    // Set up the DMA packet to clear the screen. We want to clear to blue.
    SPS2Manager.InitScreenClear(0, 0x25, 0x50);

    Pipeline.PositionCamera(Vector4(0.0f, 0.0f, 0.0f, 1.0f), Vector4(0.0f, 0.0f, -1.0f, 1.0f));

    this->isRunning = true;
    this->Initialize();
}

void Game::Stop() {
    this->isRunning = false;
}

void Game::UnloadContent() {
    int n = this->gameObjects->GetCount();
    for (int i = 0; i < n; i++) {
        this->gameObjects->GetItem(i)->UnloadContent();
    }
}

void Game::Update(float timeDelta) {
    int n = this->gameObjects->GetCount();
    for (int i = 0; i < n; i++) {
        this->gameObjects->GetItem(i)->Update(timeDelta);
    }
}